﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;

namespace OpenWaves.ActivityFeed
{
    [ContractClass(typeof(ActivityFeedServiceContract))]    
    public interface IActivityFeedService
    {
        /// <summary>
        /// Subscribe user for list of topics
        /// </summary>
        /// <param name="subscriberId"></param>
        /// <param name="topics"></param>
        void Subscribe(string subscriberId, IEnumerable<Topic> topics);
        
        /// <summary>
        /// Unsubscribe user from list of topics
        /// </summary>
        /// <param name="subscriberId"></param>
        /// <param name="topics"></param>
        void Unsubscribe(string subscriberId, IEnumerable<Topic> topics);

        Subscriber GetSubscriber(string subscriberId);
        
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        IQueryable<Subscriber> GetSubscribers();

        Update GetUpdate(string updateId);
        
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        IQueryable<Update> GetUpdates();

        IQueryable<Update> SearchUpdates(string searchTerms = null);

        /// <summary>
        /// Publish new update
        /// </summary>
        /// <param name="content">custom content of update</param>
        /// <param name="publisherId">id of user who is the author of update</param>
        /// <param name="source">source of update</param>
        /// <param name="topics">list of topics related with update</param>
        /// <param name="isPublic">if true, then update is marked as public, and update will be visible for all users, otherwise it will be limited by userGroups</param>
        /// <param name="userGroups">list of roles or logins that will limit the access to update</param>
        /// <returns></returns>
        Update PublishUpdate(UpdateContent content, string publisherId, string source, IEnumerable<Topic> topics, bool isPublic, IEnumerable<UserGroup> userGroups);

        void RemoveUpdate(Update update);

        void RemoveUpdatesOlderThan(DateTime dateTime);
    }

    [ContractClassFor(typeof(IActivityFeedService))]
    class ActivityFeedServiceContract : IActivityFeedService
    {
        public void Subscribe(string subscriberId, IEnumerable<Topic> topics)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(subscriberId) == false);
            Contract.Requires<ArgumentNullException>(topics != null);
            throw new System.NotImplementedException();
        }

        public void Unsubscribe(string subscriberId, IEnumerable<Topic> topics)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(subscriberId) == false);
            Contract.Requires<ArgumentNullException>(topics != null);
            throw new System.NotImplementedException();
        }

        public Subscriber GetSubscriber(string subscriberId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(subscriberId) == false);
            throw new System.NotImplementedException();
        }

        public IQueryable<Subscriber> GetSubscribers()
        {
            throw new System.NotImplementedException();
        }

        public IEnumerable<Topic> GetSubscriptions(string subscriberId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(subscriberId) == false);
            throw new System.NotImplementedException();
        }

        public IQueryable<Update> GetUpdates()
        {
            Contract.Ensures(Contract.Result<IQueryable<Update>>() != null);
            throw new System.NotImplementedException();
        }

        public IQueryable<Update> SearchUpdates(string searchTerms = null)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(searchTerms) == false);
            Contract.Ensures(Contract.Result<IQueryable<Update>>() != null);
            throw new System.NotImplementedException();
        }

        public Update GetUpdate(string updateId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(updateId) == false);
            throw new System.NotImplementedException();
        }

        public Update PublishUpdate(UpdateContent content, string publisherId, string source, IEnumerable<Topic> topics, bool isPublic, IEnumerable<UserGroup> userGroups)
        {
            Contract.Requires<ArgumentNullException>(content != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(publisherId) == false);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(source) == false);
            Contract.Requires<ArgumentNullException>(topics != null);
            Contract.Requires<ArgumentNullException>(userGroups != null);
            throw new System.NotImplementedException();
        }

        public void RemoveUpdate(Update update)
        {
            throw new System.NotImplementedException();
        }

        public void RemoveUpdatesOlderThan(DateTime dateTime)
        {
            Contract.Requires<ArgumentNullException>(dateTime != null, "DateTime has to be assigned.");
            throw new NotImplementedException();
        }
    }

    public static class ActivityFeedServiceExtensions
    {
        public static Update PublishPublicUpdate(this IActivityFeedService service, UpdateContent content, string publisherId, string source, params Topic[] topics)
        {
            return service.PublishUpdate(content, publisherId, source, topics, true, Enumerable.Empty<UserGroup>());
        }

        public static Update PublishPublicUpdate(this IActivityFeedService service, UpdateContent content, string publisherId, string source, IEnumerable<Topic> topics)
        {
            return service.PublishUpdate(content, publisherId, source, topics, true, Enumerable.Empty<UserGroup>());
        }
    }
}